package recorder
{
	import collisions.LinkedList;
	import com.demonsters.debugger.MonsterDebugger;
	import entities.Entity;
	import flash.geom.Point;
	import recorder.RecordData;
	/**
	 * ...
	 * @author Gareth Williams
	 * this recorder attempts to manage a sparse collection of recorded values for spatial properties on an entity
	 * by using a linked list to record only change in values and then interpolating between them to 
	 * retrieve a value for a timestep, we avoid using memory for redundant data
	 */
	public class EntityPositionRecorder implements IRecorder
	{
		public var isRecording:Boolean = false;
		
		private const properties:Array = ["x", "y"];
		
		private var what:Entity;
		
		private var previousPosition:Point 	 = new Point();
		private var previousDelta:Point	 	 = new Point();
		
		private var recordingHead:LinkedList = new LinkedList(new RecordData(Main.timestep));
		private var recordingTail:LinkedList = recordingHead;
		
		public function EntityPositionRecorder(what:Entity) 
		{
			this.what = what;
		}
		
		/* INTERFACE recorder.IRecorder */
		
		public function update():void
		{
			var data:RecordData = new RecordData(Main.timestep);
			var isRepeatData:Boolean = true;//flag whether to bother adding this record...
			for each (var spatialProperty:String in properties)
			{	
				var currentValue:Number = what[spatialProperty];
				var currentDelta:Number = currentValue - previousPosition[spatialProperty];
				if (previousDelta[spatialProperty] !== currentDelta)
				{
					isRepeatData = false;
				}
				data[spatialProperty] = currentValue;
				previousDelta[spatialProperty] = currentValue - previousPosition[spatialProperty];
				previousPosition[spatialProperty] = currentValue;
			}
			
			if (isRepeatData) return;
			
			//insert into list at correct timestep...
			var recordIterator:LinkedList = recordingHead;
			while(recordIterator.next !== null)
			{
				if (RecordData(recordIterator.next.object).timestep > Main.timestep) break;
				recordIterator = recordIterator.next;
			}
			recordingTail = recordIterator;
			
			//overwrite or insert
			if (RecordData(recordIterator.object).timestep === Main.timestep)
			{
				recordingTail.object = data;
			}
			else //insert or add to the end (insert between last item and null next item)
			{
				var insert:LinkedList = new LinkedList(data);
				insert.next = recordingTail.next;
				recordingTail.next = insert;
			}
		}
		
		public function retrieveDataAt(timestep:Number):RecordData
		{
			//find nearest recordIterator to timestep
			var recordIterator:LinkedList = recordingHead;
			while(recordIterator.next !== null)
			{
				if (RecordData(recordIterator.next.object).timestep > timestep) break;
				recordIterator = recordIterator.next;
			}
			
			//shortcut early if we are at the end of the list or have a record of this position...
			if (recordIterator.next === null ||
				RecordData(recordIterator.object).timestep == timestep)
			{
				return RecordData(recordIterator.object);
			}
			
			//interpolate properties for timestep
			var prevData:RecordData 		= RecordData(recordIterator.object);
			var nextData:RecordData 		= RecordData(recordIterator.next.object);
			var interpolatedData:RecordData = new RecordData(timestep);
			
			var tDelta:Number = nextData.timestep - prevData.timestep;
			for each (var spatialProperty:String in properties)
			{
				//linear interpolation
				var pDelta:Number = nextData[spatialProperty] - prevData[spatialProperty];
				var unit:Number = pDelta / Math.abs(tDelta);
				interpolatedData[spatialProperty] = prevData[spatialProperty] + Math.abs(timestep - prevData.timestep) * unit;
			}
			return interpolatedData;
		}
		
		public function getLastTimestep():Number
		{
			var recordIterator:LinkedList = recordingHead;
			while(recordIterator.next !== null) recordIterator = recordIterator.next;
			return RecordData(recordIterator.object).timestep;
		}
		
		public function getFirstTimestep():Number
		{
			return RecordData(recordingHead.object).timestep;
		}
		
		public function rewind():void
		{
			recordingTail = recordingHead;
		}
	}
}